#pragma once
#include "Sem.hpp"
#include "_Mutex.hpp"
#include <iostream>
#include <unistd.h>
#include <vector>

using namespace SemModule;
using namespace MutexModule;

template <class T>
class RingQueue
{
public:
    RingQueue(int cap = default_num)
        :_cap(cap),
        _v(cap),    //对于自定义类而言，通过参数化列表构造会自动调用其构造函数，这里就把vector的大小初始化好了
        _blank_sem(cap),//生产者初始信号量大小为5
        _P_step(0),//生产者数组下标
        _data_sem(0),//消费者初始信号量大小为0
        _C_step(0)//消费者数组下标
    {
    }
    void Equeue(const T& in)
    {
        //生产者申请信号量，本质是让它做--，因为生产者本身信号量初始值就为5
        _blank_sem.P();
        _plock.Lock();
        
        
        //生产，入数据
        _v[_P_step] = in;

        //更新数组下标
        _P_step++;
        _P_step %= _cap;//使之成环

        _plock.UnLock();

        //消费者信号量++，这里就能唤醒等待的消费者线程，让其执行后续代码
        _data_sem.V();
    }
    
    void Pop(T* out)
    {
        //消费者申请信号量，因为消费者的初始信号量大小为0，所以会阻塞等待，直到生产者生产一个数据，并释放
        _data_sem.P();
        _clock.Lock();
        //保存数据，保存数据后当前下标就没有用了。
        *out = _v[_C_step];

        //更新下表
        _C_step++;
        _C_step %= _cap;
        //生产者信号量++
        _clock.UnLock();
        _blank_sem.V();//当消费者拿完一个数据时，当前位置就空下来了，所以生产的信号量要++
    }

    ~RingQueue(){}

private:
    std::vector<T> _v;
    int _cap;

    //生产者
    Sem _blank_sem; //生产者信号量
    int _P_step; //生产者对应下标，为了不让消费者超过生产者

    //消费者
    Sem _data_sem; //消费者信号量
    int _C_step;
    
    //维护多生产，多消费需要两把锁
    Mutex _plock;
    Mutex _clock; 
};